package cc.chinagps.gateway.aplan.pkg;

import java.io.IOException;
import java.util.List;

import org.seg.lib.util.Util;

import cc.chinagps.gateway.aplan.pkg.RouteTable.Node;
import cc.chinagps.gateway.aplan.pkg.bodys.DeliverAckBody;
import cc.chinagps.gateway.aplan.pkg.bodys.DeliverBody;
import cc.chinagps.gateway.aplan.pkg.bodys.NetStatusReportAckBody;
import cc.chinagps.gateway.aplan.pkg.bodys.NetStatusReportBody;
import cc.chinagps.gateway.aplan.pkg.bodys.SubmitAckBody;
import cc.chinagps.gateway.aplan.pkg.bodys.SubmitBody;
import cc.chinagps.gateway.util.Constants;

public class APlanPackage {
	private APlanHead head;
	
	private RouteTable routeTable;
	
	private byte[] data;
	
	private int bodyStart;

	public int getBodyStart() {
		return bodyStart;
	}

	public void setBodyStart(int bodyStart) {
		this.bodyStart = bodyStart;
	}

	public byte[] getData() {
		return data;
	}

	public void setData(byte[] data) {
		this.data = data;
	}

	public APlanHead getHead() {
		return head;
	}

	public void setHead(APlanHead head) {
		this.head = head;
	}

	public RouteTable getRouteTable() {
		return routeTable;
	}

	public void setRouteTable(RouteTable routeTable) {
		this.routeTable = routeTable;
	}
	
	public static APlanPackage parseFrom(byte[] data) throws IOException{
		APlanHead head = new APlanHead();

		head.setCommandLength(Util.getInt(data, 0));
		head.setCommandId(Util.getInt(data, 4));
		head.setCommandStatus(Util.getInt(data, 8));
		head.setCommandExecute(Util.getInt(data, 12));
		head.setCommandType(Util.getInt(data, 16));
		head.setSequenceNo(Util.getInt(data, 20));
		
		byte centerSourceId_length = data[24];
		head.setCenterSourceId(new String(data, 25, centerSourceId_length));
		int position = 25 + centerSourceId_length;
		
		head.setCenterSourceType(Util.getInt(data, position));
		position += 4;
		
		byte centerDesId_length = data[position];
		position += 1;
		head.setCenterDesId(new String(data, position, centerDesId_length));
		position += centerDesId_length;
		
		head.setCenterDestType(Util.getInt(data, position));
		position += 4;
		
		RouteTable routeTable = new RouteTable();
		int routeSize = Util.getInt(data, position);
		position += 4;
		routeTable.setCurrentPointer(Util.getInt(data, position));
		position += 4;
		
		List<Node> nodeList = routeTable.getNodeList();
		for(int i = 0; i < routeSize; i++){
			int nodeType = Util.getInt(data, position);
			position += 4;
			int nodeId = Util.getInt(data, position);
			position += 4;
			String nodeIP = APlanUtil.getCString(data, position, 16);
			position += 16;
			
			Node node = new Node();
			node.setNodeId(nodeId);
			node.setNodeIP(nodeIP);
			node.setNodeType(nodeType);
			nodeList.add(node);
		}
		
		APlanPackage pkg = new APlanPackage();
		pkg.setData(data);
		pkg.setHead(head);
		pkg.setRouteTable(routeTable);
		pkg.setBodyStart(position);
		
		return pkg;
	}
	
	public static byte[] encode(APlanHead head, RouteTable routeTable, byte[] bodyData){
		int headLength = 34;
		String centerSourceId = head.getCenterSourceId();
		String centerDesId = head.getCenterDesId();
		byte[] centerSourceId_data = null;
		byte[] centerDesId_data = null;
		if(centerSourceId != null){
			centerSourceId_data = centerSourceId.getBytes();
			headLength += centerSourceId_data.length;
		}
		
		if(centerDesId != null){
			centerDesId_data = centerDesId.getBytes();
			headLength += centerDesId_data.length;
		}
		
		int routeTableLength = 8 + 24 * routeTable.getNodeList().size();

		int totalLength = headLength + routeTableLength + bodyData.length;
		byte[] data = new byte[totalLength];
		int position = 0;
		//Command_Length
		position = APlanUtil.copyData(Util.getIntByte(totalLength), data, position);
		//Command_ID
		position = APlanUtil.copyData(Util.getIntByte(head.getCommandId()), data, position);
		//Command_Status
		position = APlanUtil.copyData(Util.getIntByte(head.getCommandStatus()), data, position);
		//Command_Execute
		position = APlanUtil.copyData(Util.getIntByte(head.getCommandExecute()), data, position);
		//Command_Type
		position = APlanUtil.copyData(Util.getIntByte(head.getCommandType()), data, position);
		//Sequence_No
		position = APlanUtil.copyData(Util.getIntByte(head.getSequenceNo()), data, position);
		//Center_Source_ID
		if(centerSourceId != null){
			data[position] = (byte) centerSourceId_data.length;
			position += 1;
			position = APlanUtil.copyData(centerSourceId_data, data, position);
		}else{
			data[position] = 0;
			position += 1;
		}
		//Center_Source_Type
		position = APlanUtil.copyData(Util.getIntByte(head.getCenterSourceType()), data, position);
		//Center_Des_ID
		if(centerDesId != null){
			data[position] = (byte) centerDesId_data.length;
			position += 1;
			position = APlanUtil.copyData(centerDesId_data, data, position);
		}else{
			data[position] = 0;
			position += 1;
		}
		//Center_Dest_Type
		position = APlanUtil.copyData(Util.getIntByte(head.getCenterDestType()), data, position);
		
		//routeTable
		List<Node> routeTable_nodeList = routeTable.getNodeList();
		position = APlanUtil.copyData(Util.getIntByte(routeTable.getNodeList().size()), data, position);
		position = APlanUtil.copyData(Util.getIntByte(routeTable.getCurrentPointer()), data, position);
		for(int i = 0; i < routeTable_nodeList.size(); i++){
			Node node = routeTable_nodeList.get(i);
			//Node_Type
			position = APlanUtil.copyData(Util.getIntByte(node.getNodeType()), data, position);
			//Node_ID
			position = APlanUtil.copyData(Util.getIntByte(node.getNodeId()), data, position);
			//Node_IP
			APlanUtil.copyData(node.getNodeIP().getBytes(), data, position);
			position += 16;
		}
		
		APlanUtil.copyData(bodyData, data, position);
		return data;
	}
	
	
	
	public static void main(String[] args) throws Exception {
		//submit
		//byte[] data = new byte[]{0x00, 0x00, 0x00, 0x75, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, (byte) 0x80, 0x00, 0x00, 0x5b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x39, 0x30, 0x2e, 0x30, 0x2e, 0x31, 0x32, 0x2e, 0x31, 0x34, 0x39, 0x00, 0x00, 0x00, 0x33, 0x39, 0x31, 0x33, 0x38, 0x32, 0x37, 0x34, 0x32, 0x37, 0x39, 0x34, 0x31, 0x00, 0x00, 0x03, (byte) 0xe7, (byte) 0x9e, 0x78, 0x03, (byte) 0xe7, (byte) 0x9e, 0x78, 0x01, 0x00, 0x00, 0x00, 0x19, 0x5b, 0x00, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x0b, 0x31, 0x33, 0x38, 0x32, 0x37, 0x34, 0x32, 0x37, 0x39, 0x34, 0x31, 0x5d};
		//submit ack
		//byte[] data = new byte[]{0x00, 0x00, 0x00, 0x66, (byte) 0x80, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, (byte) 0x80, 0x00, 0x00, 0x5b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x39, 0x30, 0x2e, 0x30, 0x2e, 0x31, 0x32, 0x2e, 0x31, 0x34, 0x39, 0x00, 0x00, 0x00, 0x33, 0x39, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x39, 0x30, 0x2e, 0x30, 0x2e, 0x31, 0x32, 0x2e, 0x31, 0x34, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x33, 0x38, 0x32, 0x37, 0x34, 0x32, 0x37, 0x39, 0x34, 0x31, 0x00};
				
		//deliver
		byte[] data = new byte[]{0x00, 0x00, 0x00, (byte) 0x8f, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x39, 0x30, 0x2e, 0x30, 0x2e, 0x31, 0x32, 0x2e, 0x31, 0x34, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x33, 0x38, 0x32, 0x37, 0x34, 0x32, 0x37, 0x39, 0x34, 0x31, 0x00, 0x03, 0x03, (byte) 0xe7, (byte) 0x9e, 0x78, 0x03, (byte) 0xe7, (byte) 0x9e, 0x78, 0x01, 0x00, 0x00, 0x00, 0x19, 0x5b, (byte) 0x80, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x31, 0x0b, 0x31, 0x33, 0x38, 0x32, 0x37, 0x34, 0x32, 0x37, 0x39, 0x34, 0x31, 0x5d, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0b, 0x39, 0x30, 0x2e, 0x30, 0x2e, 0x31, 0x32, 0x2e, 0x31, 0x34, 0x39, 0x00, 0x00, 0x00, 0x04, 0x34, 0x32, 0x33, 0x31};
		//deliver ack
		//byte[] data = new byte[]{0x00, 0x00, 0x00, 0x66, (byte) 0x80, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x27, 0x0f, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x39, 0x30, 0x2e, 0x30, 0x2e, 0x31, 0x32, 0x2e, 0x31, 0x34, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x39, 0x30, 0x2e, 0x30, 0x2e, 0x31, 0x32, 0x2e, 0x31, 0x34, 0x39, 0x00, 0x30, 0x30, 0x30, 0x32, 0x31, 0x33, 0x38, 0x32, 0x37, 0x34, 0x32, 0x37, 0x39, 0x34, 0x31, 0x00};
		
		//link test
		//byte[] data = new byte[]{0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x39, 0x30, 0x2e, 0x30, 0x2e, 0x31, 0x32, 0x2e, 0x31, 0x34, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00};
		//link test ack
		//byte[] data = new byte[]{0x00, 0x00, 0x00, 0x5a, (byte) 0x80, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x39, 0x30, 0x2e, 0x30, 0x2e, 0x31, 0x32, 0x2e, 0x31, 0x34, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x39, 0x30, 0x2e, 0x30, 0x2e, 0x31, 0x32, 0x2e, 0x31, 0x34, 0x39, 0x00, 0x00, 0x00, 0x75, 0x31};
		
		//netStatus
		//byte[] data = new byte[]{0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x39, 0x30, 0x2e, 0x30, 0x2e, 0x31, 0x32, 0x2e, 0x31, 0x34, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x39, 0x30, 0x2e, 0x30, 0x2e, 0x31, 0x32, 0x2e, 0x31, 0x34, 0x39, 0x00};
		//netStatusAck
		//byte[] data = new byte[]{0x00, 0x00, 0x00, 0x6e, (byte) 0x80, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x39, 0x30, 0x2e, 0x30, 0x2e, 0x31, 0x32, 0x2e, 0x31, 0x34, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x39, 0x30, 0x2e, 0x30, 0x2e, 0x31, 0x32, 0x2e, 0x31, 0x34, 0x39, 0x00, 0x65, 0x6e, 0x75, 0x31, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x39, 0x30, 0x2e, 0x30, 0x2e, 0x31, 0x32, 0x2e, 0x31, 0x34, 0x39, 0x00};
		
		APlanPackage pkg = APlanPackage.parseFrom(data);
		APlanHead headx = pkg.getHead();
		RouteTable rt = pkg.getRouteTable();
		
		System.out.println(headx);
		System.out.println(rt);
		
		System.out.println("commandId:" + Integer.toHexString(headx.getCommandId()));
		if(headx.getCommandId() == 3){
			System.out.println("==submit==");
			SubmitBody submitBody = SubmitBody.parseFrom(data, pkg.getBodyStart());
			System.out.println(submitBody);
			
			byte[] bodyData = submitBody.encode();
			byte[] encode = APlanPackage.encode(headx, rt, bodyData);
			boolean eq = equals(data, encode);
			System.out.println("eq:" + eq);
			
			APlanPackage re_pkg = APlanPackage.parseFrom(encode);
			APlanHead re_headx = pkg.getHead();
			RouteTable re_rt = pkg.getRouteTable();
			SubmitBody re_submitBody = SubmitBody.parseFrom(encode, re_pkg.getBodyStart());
			System.out.println("re_pkg");
			System.out.println(re_headx);
			System.out.println(re_rt);
			System.out.println(re_submitBody);
			
			byte[] re_bodyData = re_submitBody.encode();
			byte[] re_encode = APlanPackage.encode(headx, rt, re_bodyData);
			boolean re_eq = equals(encode, re_encode);
			System.out.println("eq:" + re_eq);
		}else if(headx.getCommandId() == 0x80000003){
			System.out.println("==submitAck==");
			SubmitAckBody submitAckBody = SubmitAckBody.parseFrom(data, pkg.getBodyStart());
			System.out.println(submitAckBody);
			
			byte[] bodyData = submitAckBody.encode();
			byte[] encode = APlanPackage.encode(headx, rt, bodyData);
			boolean eq = equals(data, encode);
			System.out.println("eq:" + eq);
		}else if(headx.getCommandId() == 4){
			System.out.println("==deliver==");
			DeliverBody deliverBody = DeliverBody.parseFrom(data, pkg.getBodyStart());
			System.out.println(deliverBody);
			
			byte[] bodyData = deliverBody.encode();
			byte[] encode = APlanPackage.encode(headx, rt, bodyData);
			boolean eq = equals(data, encode);
			System.out.println("eq:" + eq);
		}else if(headx.getCommandId() == 0x80000004){
			System.out.println("==deliverAck==");
			DeliverAckBody deliverAckBody = DeliverAckBody.parseFrom(data, pkg.getBodyStart());
			System.out.println(deliverAckBody);
			
			byte[] bodyData = deliverAckBody.encode();
			byte[] encode = APlanPackage.encode(headx, rt, bodyData);
			boolean eq = equals(data, encode);
			System.out.println("eq:" + eq);
		}else if(headx.getCommandId() == 5){
			System.out.println("==linkTest==");
			//byte[] bodyData = Constants.ZERO_BYTES_DATA;
			byte[] encode = APlanPackage.encode(headx, rt, Constants.ZERO_BYTES_DATA);
			boolean eq = equals(data, encode);
			System.out.println("eq:" + eq);
		}else if(headx.getCommandId() == 0x80000005){
			System.out.println("==linkTestAck==");
			
			//byte[] bodyData = Constants.ZERO_BYTES_DATA;
			byte[] encode = APlanPackage.encode(headx, rt, Constants.ZERO_BYTES_DATA);
			boolean eq = equals(data, encode);
			System.out.println("eq:" + eq);
		}else if(headx.getCommandId() == 6){
			System.out.println("==netStatusReport==");
			NetStatusReportBody netStatusReportBody = NetStatusReportBody.parseFrom(data, pkg.getBodyStart());
			System.out.println(netStatusReportBody);
			
			byte[] bodyData = netStatusReportBody.encode();
			byte[] encode = APlanPackage.encode(headx, rt, bodyData);
			boolean eq = equals(data, encode);
			System.out.println("eq:" + eq);
		}else if(headx.getCommandId() == 0x80000006){
			System.out.println("==netStatusReportAck==");
			NetStatusReportAckBody netStatusReportAckBody = NetStatusReportAckBody.parseFrom(data, pkg.getBodyStart());
			System.out.println(netStatusReportAckBody);
			
			byte[] bodyData = netStatusReportAckBody.encode();
			byte[] encode = APlanPackage.encode(headx, rt, bodyData);
			boolean eq = equals(data, encode);
			System.out.println("eq:" + eq);
		}
	}
	
	private static boolean equals(byte[] bs1, byte[] bs2){
		if(bs1.length != bs2.length){
			System.out.println("bs1.length:" + bs1.length + ", bs2.length:" + bs2.length);
			return false;
		}
		
		for(int i = 0; i < bs1.length; i++){
			if(bs1[i] != bs2[i]){
				System.out.println("i:" + i);
				return false;
			}
		}
		
		return true;
	}
}